import 'package:dio/dio.dart';
import 'package:flutter_enjoy_plus/constants/index.dart';
import 'package:flutter_enjoy_plus/utils/EventBus.dart';
import 'package:flutter_enjoy_plus/utils/Toast.dart';

import 'TokenManager.dart';

class RequestDio {
  // 1. 创建dio实例
  final Dio _dio = Dio();
  RequestDio() {
    // 2. 配置基地址和超时时间
    _dio.options.baseUrl = GlobalVariable.BASE_URL;
    _dio.options.connectTimeout =
        const Duration(seconds: GlobalVariable.TIME_OUT);
    _dio.options.receiveTimeout =
        const Duration(seconds: GlobalVariable.TIME_OUT);
    // 3. 添加拦截器
    _registerInterceptors();
  }

  // 刷新token
  Future<bool> _isRefreshTokenExpired() async {
    final refreshToken = tokenManager.getRefreshToken();
    // 新建一个dio实例，因为旧实例的token已经过期
    Dio dio = Dio();
    try {
      final res = await dio.post(
          '${GlobalVariable.BASE_URL}${HTTP_PATH.REFRESH_TOKEN}',
          options: Options(headers: {'Authorization': 'Bearer $refreshToken'}));
      if (res.data['code'] == 10000) {
        // 业务成功
        // 存储token
        tokenManager.setToken(res.data['data']['token'],
            refreshToken: res.data['data']['refreshToken']);
        return true;
      } else {
        // 业务失败
        return false;
      }
    } catch (e) {
      // 请求失败
      return false;
    }
  }

  // 4. 注册拦截器函数
  _registerInterceptors() {
    _dio.interceptors.add(
      InterceptorsWrapper(
        onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
          // 如果你想完成请求并返回一些自定义数据，你可以使用 `handler.resolve(response)`。
          // 如果你想终止请求并触发一个错误，你可以使用 `handler.reject(error)`。
          // 1. 获取token
          final token = tokenManager.getToken();
          // 2. 如果存在token，则添加token到请求头中
          if (token.isNotEmpty) {
            options.headers['Authorization'] = 'Bearer $token';
          }
          return handler.next(options);
        },
        onResponse: (Response response, ResponseInterceptorHandler handler) {
          // 如果你想终止请求并触发一个错误，你可以使用 `handler.reject(error)`。
          // 针对于http状态码结果 200-300之间为成功，其他为失败
          if (response.statusCode! >= 200 && response.statusCode! < 300) {
            return handler.next(response);
          } else {
            // 4开头的为客户端错误，5开头为服务器错误
            // 针对于401错误的处理...
            // 提示用户
            PromptAction.showErrorToast(response.statusMessage!);
            // 返回一个错误的Future / 前端和鸿蒙中为Promise
            return handler
                .reject(DioException(requestOptions: response.requestOptions));
          }
        },
        onError: (DioException error, ErrorInterceptorHandler handler) async {
          // 如果你想完成请求并返回一些自定义数据，你可以使用 `handler.resolve(response)`。
          // 401 授权失败逻辑 因为401错误被定义在了onError而不是在响应拦截器中
          if (error.response?.statusCode == 401) {
            // 一. 使用refreshToken刷新token
            // 1. 判断refreshToken是否为空
            final refreshToken = tokenManager.getRefreshToken();
            if (refreshToken.isNotEmpty) {
              // 2. 判断refreshToken是否过期
              // 利用函数&返回值判断
              bool isOK = await _isRefreshTokenExpired();
              if (isOK) {
                // 未过期且存储token和refreshToken成功
                // 根据token重新在我的页面获取用户信息
                // 方式1：发布一个事件
                // eventBus.fire(loginEvent());
                // 方式2：使用dio.fetch() 发送之前失败的请求，并返回结果
                final res = await _dio.fetch(error.requestOptions);
                return handler.resolve(res);
              } else {
                // 如果过期，清除本地token，跳转到登录页
                tokenManager.deleteToken();
                eventBus.fire(logoutEvent());
              }
            } else {
              // 如果为空，清除本地token，跳转到登录页
              tokenManager.deleteToken();
              eventBus.fire(logoutEvent());
            }

            // // 二. 不考虑使用刷新token，直接清除本地token，跳转到登录页
            // tokenManager.deleteToken();
            // // 跳转到登录页 但是非页面级别组件无法使用Navigator跳转，因为没法获取context上下文对象
            // // Navigator.pushNamed(context, routeName);
            // // 需要使用事件总线eventbus
            // // 在vue中使用的是eventbus，在鸿蒙中使用的是emitter
            // // 需要配置发布者和订阅者，可以通过官方三方库pub.dev下载eventbus
            // // 1. 发布一个事件
            // eventBus.fire(logoutEvent());
            // // 2. 在其他可以跳转的位置(首页)中订阅事件
          }
          return handler.next(error);
        },
      ),
    );
  }

  // 5. 封装请求方法
  // get
  Future get(String url, {Map<String, dynamic>? params}) async {
    final result = await _dio.get(url, queryParameters: params);
    return _handleResult(result);
  }

  // post
  Future post(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.post(url, data: data);
    return _handleResult(result);
  }

  // 上传文件
  Future uploadFile(String url, {FormData? data}) async {
    final result = await _dio.post(url, data: data);
    return _handleResult(result);
  }

  // put
  Future put(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.put(url, data: data);
    return _handleResult(result);
  }

  // delete
  Future delete(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.delete(url, data: data);
    return _handleResult(result);
  }

  // 6. 针对所有的请求结果统一处理
  _handleResult(Response<dynamic> response) {
    if (response.data['code'] == 10000) {
      return response.data['data'];
    }
    // 请求失败，抛出一个异常（程序员） + 增加提示（用户）
    // 提示
    PromptAction.showErrorToast(response.data['message']);
    throw Exception(response.data['message']);
  }
}

final requestDio = RequestDio();
